#!/usr/bin/python
# -*- coding: utf-8 -*-

import time, redis, datetime, json, pymongo, hashlib, random, os, sys
from decimal import Decimal

sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))

from exchange.utils.uuid import uuid
from exchange.com.db import db
from exchange.conf.conf import conf

'''
内存中订单的数据模型
orders = {
    "eth/usdt": {
        "buy": [
            [seqno, account, price, amount, opened],
            [seqno, account, price, amount, opened],
            ,,,
        ],
        "sell": [
            [seqno, account, price, amount, opened],
            [seqno, account, price, amount, opened],
            ,,,
        ]
    }
}
'''
orders = {}

'''
内存中交易深度的数据模型
depth = {
    "eth/usdt":{
        "buy": [
            [price, amount],
            [price, amount],
        ],
        "sell": [
            [price, amount],
            [price, amount],
        ]
    }
}
'''
depth = {}

'''
更新交易深度
* 支持增加新的交易深度
* 支持修改已经存在的交易深度 ( +amount / -amount )
* 自动清理 amount = 0.0 的交易深度
* 发布交易深度 redis 缓存
'''
def update_depth(symbol, action, price, amount):
    if symbol not in depth:
        depth[symbol] = {}
    if action not in depth[symbol]:
        depth[symbol][action] = []
        depth[symbol][action].append([price, amount])
    else:
        index = 0
        for x in depth[symbol][action]:
            if price == x[0]:
                x[1] += amount
                if x[1] == 0.0:
                    depth[symbol][action].remove(x)
                break
            if (price > x[0] and action == 'buy') or (price < x[0] and action == 'sell'):
                depth[symbol][action].insert(index, [price, amount])
                break
            index += 1
    db.set_depth(symbol, depth[symbol])



'''
撮合交易服务重启后需要从 mongodb 中提取最后的挂单状态 load 到内存中
'''
def reload():

    print("reloading...")

    for symbol in db.get_symbols():
        # 读取 orders 数据 加载到 内存中
        for action in ["buy", "sell"]:
            for o in db.get_orders_by_reload(symbol, action):
                account, price, amount, seqno = o.get("account"), Decimal(str(o.get("price"))), Decimal(str(o.get("amount"))), o.get("seqno")
                opened = amount
                for a in db.get_historys(symbol, action, seqno):
                    opened -= Decimal(str(a["amount"]))

                order(account, seqno, symbol, action, price, amount, opened, reload=True)

                # print(account, seqno, symbol, action, price, amount, opened)

    print("reload finished.")



'''
撤单
* 删除内存中该订单
* 更新交易深度
* 更新数据库中该订单为完成状态
* 添加 queue-cancel 记录
'''
def cancel(account, symbol, action, seqno):

    '''
    删除内存中该订单记录
    '''
    price, amount = 0.0, 0.0
    for x in orders[symbol][action]:
        if x[0] == seqno and x[1] == account:
            price, amount = (x[2]), (x[4])
            orders[symbol][action].remove(x)
            break

    if amount > 0.0:

        '''
        更新交易深度
        '''
        update_depth(symbol, action, price, -amount)

        '''
        更新数据库中 order 为完成状态
        '''
        db.set_order_finished(symbol, action, seqno)

        '''
        数据库中增加 queue-cancel 记录
        '''
        db.add_cancels( account, symbol, action, seqno, price, amount )


'''
下单
1 在内存中增加一个新的订单记录
2 更新交易深度
3 数据库中增加 queue-order 记录
4 执行撮合操作
'''
def order(account, seqno, symbol, action, price, amount, opened, reload=False):

    price = Decimal(price)
    amount = Decimal(amount)

    if action == 'buy' and price == 0.0:
        return
    '''
    1 内存中增加订单
    '''
    if symbol not in orders:
        orders[symbol] = {}
    if action not in orders[symbol]:
        orders[symbol][action] = []
        orders[symbol][action].append([seqno, account, price, amount, opened])
    else:
        index = 0
        for x in orders[symbol][action]:
            if (price > x[2] and action == 'buy') or (price < x[2] and action == 'sell'):
                orders[symbol][action].insert(index, [seqno, account, price, amount, opened])
                break
            index += 1

    '''
    2 更新交易深度
    '''
    update_depth(symbol, action, price, amount)

    if not reload:
        '''
        3 数据库增加 queue-order 记录
        '''
        db.add_orders( account, symbol, action, seqno, price, amount )

        '''
        4 执行撮合操作
        '''
        merge(symbol, action)



'''
撮合交易
1 更新内存中订单 (买卖同时更新) 抵消后买方减少、卖方减少 (基于双方的最小值)
2 更新交易深度 (买卖盘同时更新)
3 数据库中增加 history 历史交易记录
4 检测订单完全被交易后 更新数据中该订单的 finished 状态
'''
def merge(symbol, action):

    buys    = orders[symbol]['buy']
    sells   = orders[symbol]['sell']

    # 撮合抵消
    while 1:

        if len(buys) == 0 or len(sells) == 0:
            break

        b_seqno, b_account, b_price, b_amount, b_opened = buys[0]
        s_seqno, s_account, s_price, s_amount, s_opened = sells[0]

        if b_price < s_price:
            break

        deal_price  = s_price if action == 'buy' else b_price
        deal_amount = min(b_opened, s_opened)

        # print(deal_price, deal_amount)

        buys [0][4] -= deal_amount
        sells[0][4] -= deal_amount

        update_depth(symbol, 'buy',  b_price, -deal_price)
        update_depth(symbol, 'sell', s_price, -deal_price)

        db.add_history(symbol, action, b_account, s_account, b_seqno, s_seqno, deal_price, deal_amount)

        print(symbol, action, b_account, s_account, b_seqno, s_seqno, deal_price, deal_amount)

        # 释放已经完成撮合的空单
        for action in ['buy', 'sell']:
            _seqno, _account, _price, _amount, _opened = orders[symbol][action][0]
            if _opened == 0.00:
                db.set_order_finished(symbol, action, _seqno)
                orders[symbol][action].pop(0)


'''
主线
1 查询数据库中的 订单 数据 加载到内存中
2 接收 queue 数据 处理 order & cancel 消息
'''
def main():

    reload()

    print("match servcie is started.")

    while 1:

        q = db.rdb.rpop("queue")
        if not q:
            time.sleep(0.1)
            continue

        queue = q.split(",")

        # 撤单
        if queue[0] == "cancel":
            account, symbol, action, seqno = queue[1:]
            cancel(account, symbol, action, seqno)
            continue

        # 下单
        if queue[0] == "order":
            account, symbol, action, price, amount = queue[1:]
            opened = amount
            seqno = uuid.get_tick()
            order(account, seqno, symbol, action, price, amount, opened, reload=False)
            continue



'''
入口
'''
if __name__ == '__main__':
    main()
